﻿using System.Collections.Generic;
using Romulus;
using Romulus.Nes;
using SolKeyTest.Data;

namespace SolKeyTest
{
    /// <summary>
    /// Demonstrates 
    /// </summary>
    public class SolScreenControl:ScreenControl
    {
        // Helper to expose Rom as our specific ROM type.
        internal new SolRom Rom { get { return base.Rom as SolRom; } set { base.Rom = value; } }

        // What level to render;
        public int LevelNum { get; set; }
        private List<SpriteDef> ModifiedSprites = new List<SpriteDef>();

        // This is where we prepare to render.
        protected override void BeforeRender() {
            base.BeforeRender();

            // This is where you should set you bg patterns (and sprite patterns, if applicable)
            // Each level may have its own background pattern
            Patterns = Rom.GetLevel(LevelNum).BackgroundPatterns.PatternImage;
            SpritePatterns = Rom.spritePatterns.PatternImage;

        }

        protected override void LoadBgPalette(ReturnEventArgs<CompositePalette> e) {
            base.LoadBgPalette(e);

            e.ReturnValue = palette;
        }
        protected override void LoadSpritePalette(ReturnEventArgs<CompositePalette> e) {
            base.LoadSpritePalette(e);

            e.ReturnValue = spritePalette;
            // This is where we would return the sprite palette
        }
        
        protected override void ConstructNametable() {
            base.ConstructNametable();
            Level level = Rom.GetLevel(LevelNum);

            // Load background tile for this level
            for (int palx = 0; palx < 2; palx++)
            {
                for (int paly = 0; paly < 2; paly++)
                {
                    int baseX = 16 * palx; // Location of this quadrant
                    int baseY = 16 * paly;
                    int pal = palx + paly * 2; // Palette for this quadrant

                    // Draw all tiles for this quadrant (16x16 tiles)
                    for (int i = 0; i < 16; i++)
                    {
                        for (int j = 0; j < 16; j++)
                        {
                            NameTable[i + baseX, j + baseY] = SolKeyTiles.BackgroundTile[0,0];
                        }
                    }
                }
            }

            // Load border
            // Load magic blocks
            LoadBlocksInLevel(level, level.MagicBlocks, SolKeyTiles.MagicBlock);
            // Load solid blocks
            LoadBlocksInLevel(level, level.SolidBlocks, SolKeyTiles.SolidBlock);

            // Load exit
            AddTileBytePos(SolKeyTiles.Door, level.Exit);
            AddTileBytePos(SolKeyTiles.Key, level.KeyPosition);

            // Load spawn points
            AddTileBytePos(SolKeyTiles.SpawnShield, level.SpawnPos1);
            AddTileBytePos(SolKeyTiles.SpawnShield, level.SpawnPos2);

            foreach (SolKeyLevelElement e in level.ItemsList)
            {
                TileEntry[,] ItemTiles = SolKeyTiles.GetTileByIndex((byte)(e.ElementIndex & 0x3F));
                if (ItemTiles != null)
                    AddTileBytePos(ItemTiles, e.Position);
            }

        }

        private void AddTileBytePos(TileEntry[,] Tile, byte Position)
        {
            int xPos = Position & 0x0F;
            int yPos = (Position & 0xF0) >> 4;
            AddTileBlockToNameTable(Tile, xPos, yPos * 2);
        }

        private void LoadBlocksInLevel(Level level, byte[] BlockArray, TileEntry[,] BlockTile)
        {
            int yPos = 2;
            for (int i = 0; i < BlockArray.Length; i++)
            {
                // for the right side of the screen, we want to offset the xpos by 8 blocks
                int xPosOffset = 0;

                if (i % 2 != 0)
                {
                    xPosOffset = 8;
                }
                byte b = BlockArray[i];
                for (int xPos = 0; xPos < 16; xPos += 1)
                {
                    // Add block to table at the appropriate x,y
                    if (BlockAtXPos(b, xPos))
                        AddTileBlockToNameTable(BlockTile, xPos + xPosOffset, yPos);
                }
                if (i % 2 != 0)
                {
                    yPos += 2;
                }
            }
        }

        private void AddTileBlockToNameTable(TileEntry[,] tiles, int xPos, int yPos)
        {
            for (int y = 0; y < 2; y++)
            {
                for (int x = 0; x < 2; x++)
                {
                    NameTable[(xPos * 2 + x), (yPos + y)] = tiles[y,x];
                }
            }
        }

        private bool BlockAtXPos(byte data, int xPos)
        {
            return ((data & (0x80 >> xPos)) >> (7-xPos) == 1);
        }

        protected override void ConstructSpriteList(EventArgs<NameTableRenderer> e) {
            // This is where you would construct your sprite list
            // (You can safely ignore e and use the this.NameTable property)
            base.ConstructSpriteList(e);

            foreach(SpriteDef def in ModifiedSprites)
            {
                foreach (SpriteTile tile in def.Data)
                {
                    e.Value.Sprites.Add(tile);
                }
            }

            //// We are going to draw all tiles, with a different palette in each quadrant
            //for (int palx = 0; palx < 2; palx++)
            //{
            //    for (int paly = 0; paly < 2; paly++)
            //    {
            //        int baseX = 16 * palx; // Location of this quadrant
            //        int baseY = 16 * paly;
            //        int pal = palx + paly * 2; // Palette for this quadrant
            //        // Draw all tiles for this quadrant (16x16 tiles)
            //        for (int i = 0; i < 16; i++)
            //        {
            //            for (int j = 0; j < 16; j++)
            //            {
            //                e.Value.Sprites.Add(new SpriteTile((byte)(i + j * 16), (byte)((i + baseX) * 8), (byte)((j + baseY) * 8), Blitter.FlipFlags.None, (byte)pal));
            //            }
            //        }
            //    }
            //}
        }

        //protected override void OnMouseWheel(System.Windows.Forms.MouseEventArgs e)
        //{
        //    base.OnMouseWheel(e);
        //    if (e.Delta > 0)
        //    {
        //        int i = e.Delta;
        //        do
        //        {
        //            NextLevel();
        //            i--;
        //        } while (i > 0);
        //        RenderScreen();
        //    }
        //    else if (e.Delta < 0)
        //    {
        //        int i = e.Delta;
        //        do
        //        {
        //            PrevLevel();
        //            i++;
        //        } while (i > 0);
        //        RenderScreen();
        //    }
        //}

        protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
        {
            base.OnKeyDown(e);
            bool rerenderScreen = false;
            if (e.Shift && e.KeyCode == System.Windows.Forms.Keys.Delete)
            {
                rerenderScreen = true;
                ModifiedSprites.Clear();
            }
            else if (e.KeyCode == System.Windows.Forms.Keys.PageDown)
            {
                rerenderScreen = NextLevel();
            }
            else if (e.KeyCode == System.Windows.Forms.Keys.PageUp)
            {
                rerenderScreen = PrevLevel();
            }

            if (rerenderScreen)
                this.RenderScreen();
        }

        private bool NextLevel()
        {
            if (LevelNum < SolRom.MAX_LEVEL)
            {
                LevelNum++;
                return true;
            }
            return false;
        }

        private bool PrevLevel()
        {
            if (LevelNum > SolRom.MIN_LEVEL)
            {
                LevelNum--;
                return true;
            }
            return false;
        }

        protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseDown(e);
            // Add a block to the screen
            // Align the block to the grid.  Translate x and y to grid coordinates
            byte gridX = (byte)((e.X/16) * 16);
            byte gridY = (byte)((e.Y/16) * 16);
            
            SpriteDef def = new SpriteDef(new SpriteTile[] 
                {
                    new SpriteTile((byte)0x10, gridX, gridY, Blitter.FlipFlags.None),
                    new SpriteTile((byte)0x12, (byte)(gridX + 8), gridY, Blitter.FlipFlags.None),
                    new SpriteTile((byte)0x01, gridX, (byte)(gridY + 8), Blitter.FlipFlags.None),
                    new SpriteTile((byte)0x03, (byte)(gridX + 8), (byte)(gridY + 8), Blitter.FlipFlags.None)
                }
            );
            ModifiedSprites.Add(def);
            // Add block on screen at x, y


            this.RenderScreen();
        }

        #region Static
        // This data would normally come from the rom

        static byte[] spritePaletteData = new byte[] {
            0x0, 0x26, 0x29, 0x30, 
            0x0, 0x30, 0x16, 0x27, 
            0x0, 0x16, 0x10, 0x30, 
            0x0, 0x2c, 0x26, 0x38
        };

        static byte[] paletteData = new byte[] { 
            0xF, 0x7, 0x10, 0x30, 
            0xF, 0x7, 0x27, 0x30, 
            0xF, 0x7, 0x2c, 0x30, 
            0xF, 0x7, 0x27, 0x38
        };
        static CompositePalette palette;
        static CompositePalette spritePalette;

        static SolScreenControl() {
            palette = new CompositePalette(paletteData, 0);
            spritePalette = new CompositePalette(spritePaletteData, 0);
        }
        #endregion
    }
}
